use async_graphql::*;
use serde::{Serialize, Deserialize};
use chrono::NaiveDateTime;
use validator::Validate;

use crate::service::user::UserService;
use crate::entity::page::Page;

#[derive(Debug, Clone, CRUDTable, Serialize, Deserialize, Default)]
pub struct SysUser {
    pub id: Option<u32>,
    pub account: Option<String>,
    pub name: Option<String>,
    pub password: Option<String>,
    pub version: Option<u32>,
    pub is_delete: Option<u8>,
    pub position: Option<u32>,
    pub create_by: Option<u32>,
    pub create_time: Option<NaiveDateTime>,
    pub update_by: Option<u32>,
    pub update_time: Option<NaiveDateTime>
}

#[Object]
impl SysUser {
    async fn id(&self) -> Option<u32> { self.id }
    async fn account(&self) -> Option<String> { self.account.clone() }
    async fn password(&self) -> Option<String> { self.password.clone() }
    async fn name(&self) -> Option<String> { self.name.clone() }
    async fn version(&self) -> Option<u32> { self.version }
    async fn is_delete(&self) -> Option<u8> { self.is_delete }
    async fn position(&self) -> Option<u32> { self.position }
    async fn position_text(&self) -> Option<String> {
        match self.position {
            Some(1u32) => Some("admin".to_string()),
            Some(2u32) => Some("boss".to_string()),
            Some(3u32) => Some("manager".to_string()),
            Some(4u32) => Some("leader".to_string()),
            Some(5u32) => Some("worker".to_string()),
            _ => None
        }
    }
    async fn create_by(&self) -> Option<u32> { self.create_by }
    async fn create_time(&self) -> Option<i64> {
        match self.create_time {
            Some(d) => Some(d.timestamp()),
            _ => None
        }
    }
    async fn update_by(&self) -> Option<u32> { self.update_by }
    async fn update_time(&self) -> Option<i64> {
        match self.update_time {
            Some(d) => Some(d.timestamp()),
            _ => None
        }
    }

    pub async fn find_user_by_account(&self, account: String) -> Option<Self> {
        UserService::by_account(account).await
    }

    pub async fn find_user_list_page(&self, page: u64) -> Page<Self> {
        UserService::list_page(page).await.unwrap().into()
    }

    pub async fn delete_user_by_id(&self, ctx: &Context<'_>, id: u32) -> bool {
        let token = ctx.data::<String>().unwrap_or(&"".to_string()).to_string();
        UserService::delete(id, token).await
    }

    pub async fn save(&self, ctx: &Context<'_>, ob: InputSaveUser) -> Result<bool> {
        let token = ctx.data::<String>().unwrap_or(&"".to_string()).to_string();
        UserService::save(ob, token).await
    }

    pub async fn update(&self, ctx: &Context<'_>, ob: InputUpdateUser) -> Result<bool> {
        let token = ctx.data::<String>().unwrap_or(&"".to_string()).to_string();
        UserService::update(ob, token).await
    }
}

impl SysUser {
    pub fn new() -> SysUser {
        SysUser {
            id: None,
            account: None,
            name: None,
            password: None,
            version: None,
            is_delete: None,
            position: None,
            create_by: None,
            create_time: None,
            update_by: None,
            update_time: None
        }
    }
}

#[derive(InputObject, Debug, Validate)]
pub struct InputSaveUser {
    #[validate(required, length(max = 20, min = 1))]
    pub account: Option<String>,
    #[validate(required, length(max = 10, min = 1))]
    pub name: Option<String>,
    #[validate(required, length(max = 10, min = 1))]
    pub password: Option<String>,
    #[validate(required, range(min = 1, max = 5))]
    pub position: Option<u32>
}

#[derive(InputObject, Debug, Validate)]
pub struct InputUpdateUser {
    pub id: Option<u32>,
    #[validate(required, length(max = 20, min = 1))]
    pub account: Option<String>,
    #[validate(required, length(max = 10, min = 1))]
    pub name: Option<String>,
    #[validate(required, range(min = 1, max = 5))]
    pub position: Option<u32>,
    pub version: u32
}